package com.shop.shopserver.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.shop.shopcommon.constant.ElasticSearchConstant;
import com.shop.shopcommon.context.SecurityContext;
import com.shop.shopcommon.exception.Base.UserNotLoginException;
import com.shop.shopcommon.exception.Base.WordSegmentException;
import com.shop.shopcommon.exception.Base.RedisNumNotFoundException;
import com.shop.shopcommon.properties.ElasticSearchProperties;
import com.shop.shopcommon.utils.ElasticsearchOperate;
import com.shop.shoppojo.dto.GoodsSearchDTO;
import com.shop.shoppojo.entity.Category;
import com.shop.shoppojo.entity.Goods;
import com.shop.shoppojo.vo.GoodsSearchVO;
import com.shop.shoppojo.vo.HomeDataListVO;
import com.shop.shopserver.mapper.CategoryMapper;
import com.shop.shopserver.service.SearchService;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.get.*;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.*;

import static com.shop.shopcommon.constant.ElasticSearchConstant.*;
import static com.shop.shopcommon.constant.MessageConstant.*;
import static com.shop.shopcommon.constant.RedisConstant.USER_HISTORY_KEYWORDS_COUNT;

@Service
@Slf4j
public class SearchServiceImpl implements SearchService {
    @Autowired
    private ElasticsearchOperate elasticsearchOperate;
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    @Autowired
    private ElasticSearchProperties elasticsearchProperties;
    @Autowired
    private RestHighLevelClient client;
    @Autowired
    private CategoryMapper categoryMapper;
    /**
     * 将用户搜索词条保存到es中
     */
    public Boolean saveUserSearchWord(String searchText) throws Exception {
        // 将要存储的用户搜索词条数
        int max = ElasticSearchConstant.SAVE_USER_HISTORY_KEYWORDS_NUM;
        String username = SecurityContext.getUsername();

        // 判断索引库是否存在
        if (!elasticsearchOperate.isExistsIndex(USER_SEARCH_WORD_INDEX_NAME)) {
            log.info(INDEX_NOT_EXISTS);
            return Boolean.FALSE;
        }

        // 分词请求
        List<String> tokenValues = elasticsearchOperate.applyWordSegment(IK_MAX_WORD, searchText);
        // 判断结果是否为空
        if (tokenValues==null) {
            throw new WordSegmentException(WORD_SEGMENT_FAIL);
        }

        // 将tokens存入es所有用户搜索历史记录表,文档id为用户名+"-"+redis计算器value，字段为searchWord，value为token，
        // 使用全量更新如果不存在则插入文档，存在则更新文档
        // 1.1创建BulkRequest批量操作
        BulkRequest bulkRequest = new BulkRequest();

        for (String token : tokenValues) {
            // redis计数器自增 key为redis计算器key前缀+用户名
            redisTemplate.opsForValue().increment(USER_HISTORY_KEYWORDS_COUNT + username);

            // 获取最新的redis计数器值,注意不会抛出异常
            String redisNum = redisTemplate.opsForValue().get(USER_HISTORY_KEYWORDS_COUNT + username);
            if (redisNum==null && redisNum.isEmpty()){
                throw new RedisNumNotFoundException(GET_REDIS_FAIL);
            }

            // 拼接用户es存储用户搜索记录的文档id和userId的值
            int redisNumMod = Integer.parseInt(redisNum) % max;
            if (redisNumMod==0){
                // 如果模值为0，redisNumMod赋值SELECT_HISTORY_NUM
                redisNumMod=max;
            }
            String esWordId= username +"-"+ redisNumMod;
            log.info("esWordId:"+esWordId);
            String Doc = "{\"userId\": \""+esWordId+"\",\"searchWord\": \""+token+"\"}";
            // 配置bulkRequest
            elasticsearchOperate.addBulkRequest(bulkRequest,USER_SEARCH_WORD_INDEX_NAME,esWordId,Doc);
        }

        // 提交bulkRequest更新
        elasticsearchOperate.applyBulkRequest(bulkRequest);
        return Boolean.TRUE;
    }

    /**
     * 获取用户热搜词条，并拼接为字符串
     */
    public String getTopUserSearchWord() throws Exception {
        String username = SecurityContext.getUsername();
        // 如果用户为空，直接返回
        if (username==null || username.isEmpty()){
            throw new UserNotLoginException(USER_NOT_LOGIN);
        }
        int max = ElasticSearchConstant.SELECT_HISTORY_NUM;
        // 创建搜索请求
        SearchRequest searchRequest = new SearchRequest(USER_SEARCH_WORD_INDEX_NAME);

        // 创建搜索源构建器
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        // 设置查询结果数量
        searchSourceBuilder.size(max);

        // 构建布尔查询，用于组合多个查询条件
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        // 添加前缀查询，筛选 userId 以 15119667106 开头的数据
        PrefixQueryBuilder prefixQuery = QueryBuilders.prefixQuery("userId", username);
        boolQuery.filter(prefixQuery);

        // 设置查询条件
        searchSourceBuilder.query(boolQuery);

        // 创建 terms 聚合，按 searchWord 字段分组，并取前 10 条
        TermsAggregationBuilder aggregation = AggregationBuilders.terms("top_search_words")
                .field("searchWord")
                .size(max);

        // 添加聚合到搜索源
        searchSourceBuilder.aggregation(aggregation);

        // 将搜索源设置到搜索请求中
        searchRequest.source(searchSourceBuilder);

        // 用户词条拼接成字符串
        StringBuilder userSearchWordString = new StringBuilder();

        // 执行搜索请求
        SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);

        // 获取聚合结果
        Terms topSearchWords = searchResponse.getAggregations().get("top_search_words");

        // 遍历聚合结果并输出
        for (Terms.Bucket bucket : topSearchWords.getBuckets()) {
            String searchWord = bucket.getKeyAsString();
            long docCount = bucket.getDocCount();
            userSearchWordString.append(searchWord);
            log.info("聚合结果Search Word: " + searchWord + ", Count: " + docCount);
        }
        log.info("用户搜索词条字符串："+userSearchWordString);

        // 返回用户搜索词条字符串
        return userSearchWordString.toString();
    }

    /**
     * 搜索框商品搜索
     * @param goodsSearchDTO
     */
    @Override
    public GoodsSearchVO getSearchList(GoodsSearchDTO goodsSearchDTO) throws IOException{
        GoodsSearchVO goodsSearchVO = searchList(goodsSearchDTO);
        // 已经没有数据了，从头开始查
        if (goodsSearchVO.getList().getData()==null || goodsSearchVO.getList().getData().isEmpty()){
            // 删除page，重新查询将会根据查询类类型，page为null,将会赋初值。
            goodsSearchDTO.setPage(null);
            return this.searchList(goodsSearchDTO);
        }else {
            // 有数据则正常返回
            return goodsSearchVO;
        }
    }

    /**
     * 分类也进入商品列表 根据分类id搜索商品
     * @param goodsSearchDTO
     * @return
     * @throws IOException
     */
    public GoodsSearchVO getSearchListByCategoryId(GoodsSearchDTO goodsSearchDTO) throws IOException{
        GoodsSearchVO goodsSearchVO = searchListByCategoryId(goodsSearchDTO);
        // 已经没有数据了，从头开始查
        if (goodsSearchVO.getList().getData()==null || goodsSearchVO.getList().getData().isEmpty()){
            // 删除page，重新查询将会根据查询类类型，page为null,将会赋初值。
            goodsSearchDTO.setPage(null);
            return this.searchListByCategoryId(goodsSearchDTO);
        }else {
            // 有数据则正常返回
            return goodsSearchVO;
        }
    }

    /**
     * 分类也进入商品列表 根据分类id搜索商品
     * @param goodsSearchDTO
     */
    public GoodsSearchVO searchListByCategoryId(GoodsSearchDTO goodsSearchDTO) throws IOException {
        if (goodsSearchDTO.getCategoryId()!=null) {
            // 查询分类id节点下对应商品id
            List<Category> categoryList = categoryMapper.selectList(new LambdaQueryWrapper<Category>()
                    .eq(Category::getParentId, goodsSearchDTO.getCategoryId()));

            // 创建集合，用于存储商品id
            List<String> goodIdList = new ArrayList<>();
            categoryList.forEach(category -> {
                goodIdList.add(String.valueOf(category.getGoodsId()));
            });
            // 判断是否为空
            if (goodIdList.isEmpty()) {
                return null;
            }

            // 创建搜索请求
            SearchRequest searchRequest = new SearchRequest(GOODS_INDEX_NAME);
            // 创建搜索源构建器
            SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

            // 构建 Ids 查询
            IdsQueryBuilder idsQuery = new IdsQueryBuilder();
            goodIdList.forEach(idsQuery::addIds);
            searchSourceBuilder.query(idsQuery);

            // 添加排序，按 id 升序排列 sortType为price 按价格升序排序 sortType为sales 按销量降序排序
            if (goodsSearchDTO.getSortType()==null || goodsSearchDTO.getSortType().equals(SORT_TYPE_ID)){
                // 默认为id排序
                goodsSearchDTO.setSortType(SORT_TYPE_ID);
            }



            // 判断page是否为空
            Object[] objects = new Object[0];
            if (goodsSearchDTO.getPage() == null){
                if (Objects.equals(goodsSearchDTO.getSortType(), SORT_TYPE_ID)){
                    objects = new Object[]{0};
                }else if (Objects.equals(goodsSearchDTO.getSortType(), SORT_TYPE_PRICE)){
                    objects = new Object[]{0.00};
                }else if (Objects.equals(goodsSearchDTO.getSortType(), SORT_TYPE_SALES)){
                    objects = new Object[]{Integer.MAX_VALUE};
                }
            }else {
                // page不为空
                objects = goodsSearchDTO.getPage();
            }

            // 设置 search_after 参数
            searchSourceBuilder.size(SEARCH_GOODS_PAGE_NUM);

            if (Objects.equals(goodsSearchDTO.getSortType(), SORT_TYPE_ID)){
                // 1 默认按id排序
                SortBuilder<FieldSortBuilder> sortByIdBuilder = SortBuilders.fieldSort(GOODS_ID_FIELD).order(SortOrder.ASC);
                searchSourceBuilder.sort(sortByIdBuilder);
            }else if (Objects.equals(goodsSearchDTO.getSortType(), SORT_TYPE_PRICE)){
                // 2 按价格排序
                SortBuilder<FieldSortBuilder> sortByPriceBuilder = SortBuilders.fieldSort(GOODS_PRICE_FIELD).order(SortOrder.ASC);
                searchSourceBuilder.sort(sortByPriceBuilder);
            }else if (Objects.equals(goodsSearchDTO.getSortType(), SORT_TYPE_SALES)){
                // 3 按销量排序
                SortBuilder<FieldSortBuilder> sortBySalesBuilder = SortBuilders.fieldSort(GOODS_SALES_FIELD).order(SortOrder.DESC);
                searchSourceBuilder.sort(sortBySalesBuilder);
            }
            // 设置 search_after 参数
            searchSourceBuilder.searchAfter(objects);

            // 设置返回数量
            searchSourceBuilder.size(SEARCH_GOODS_PAGE_NUM);

            // 将搜索源构建器添加到搜索请求
            searchRequest.source(searchSourceBuilder);
            SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);

            // 解析搜索结果
            List<Goods> goodsList = new ArrayList<>();
            SearchHit[] hitsList = searchResponse.getHits().getHits();

            // 非空判断
            if (hitsList.length == 0) {
                // 没有搜索结果，更新最后文档值为初始值
                if (Objects.equals(goodsSearchDTO.getSortType(), SORT_TYPE_ID)){
                    objects = new Object[]{0};
                }else if (Objects.equals(goodsSearchDTO.getSortType(), SORT_TYPE_PRICE)){
                    objects = new Object[]{0.00};
                }else if (Objects.equals(goodsSearchDTO.getSortType(), SORT_TYPE_SALES)){
                    objects = new Object[]{Integer.MAX_VALUE};
                }
            }

            // 获取搜索结果
            for (SearchHit hit : hitsList) {
                String sourceAsString = hit.getSourceAsString();
                // 将搜索结果转换为 JSON 对象,并赋值到Goods 对象中
                JSONObject jsonObject = JSON.parseObject(sourceAsString);
                Goods goods= Goods.builder()
                        .id(Long.valueOf(jsonObject.getString(GOODS_ID_FIELD)))
                        .goodsName(jsonObject.getString(GOODS_NAME_FIELD))
                        .goodsImage(jsonObject.getString(GOODS_IMAGE_FIELD))
                        .goodsPriceMin(jsonObject.getBigDecimal(GOODS_PRICE_FIELD))
                        .goodsPriceMax(jsonObject.getBigDecimal(GOODS_PRICE_FIELD))
                        .stockTotal(jsonObject.getIntValue(GOODS_STOCK_FIELD))
                        .goodsSales(jsonObject.getIntValue(GOODS_SALES_FIELD))
                        .build();
                goodsList.add(goods);
            }

            // 获取最后一个文档的 sort 值
            SearchHits hits = searchResponse.getHits();
            SearchHit[] searchHits = hits.getHits();
            Object[] sortValues = new Object[0];
            if (searchHits.length > 0) {
                // 获取最后一个文档的 sort 值
                SearchHit lastHit = searchHits[searchHits.length - 1];
                sortValues = lastHit.getSortValues();

                // 打印 sort 值
                for (Object value : sortValues) {
                    System.out.println(value);
                }
            }

            // 封装结果
            GoodsSearchVO goodsSearchVO = GoodsSearchVO.builder()
                    .list(new HomeDataListVO(goodsList))
                    .sortValues(sortValues)
                    .build();
            return goodsSearchVO;


        }
        return null;
    }

    /**
     * 搜索框商品搜索核心逻辑
     * @param goodsSearchDTO
     */
    public GoodsSearchVO searchList(GoodsSearchDTO goodsSearchDTO) throws IOException {
        // 创建搜索请求
        SearchRequest searchRequest = new SearchRequest(GOODS_INDEX_NAME);
        // 创建搜索源构建器
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        // 创建 match_all 查询构建器
        MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery(GOODS_NAME_FIELD, goodsSearchDTO.getGoodsName())
                .analyzer(IK_MAX_WORD);
        SearchSourceBuilder query = searchSourceBuilder.query(matchQueryBuilder);

        // 判断SortType是否为空
        if (goodsSearchDTO.getSortType()==null || goodsSearchDTO.getSortType().isEmpty()){
            // 默认使用id排序
            goodsSearchDTO.setSortType(SORT_TYPE_ID);
        }

        // 判断page是否为空
        Object[] objects = new Object[0];
        if (goodsSearchDTO.getPage() == null){
            if (Objects.equals(goodsSearchDTO.getSortType(), SORT_TYPE_ID)){
                objects = new Object[]{0};
            }else if (Objects.equals(goodsSearchDTO.getSortType(), SORT_TYPE_PRICE)){
                objects = new Object[]{0.00};
            }else if (Objects.equals(goodsSearchDTO.getSortType(), SORT_TYPE_SALES)){
                objects = new Object[]{Integer.MAX_VALUE};
            }
        }else {
            // page不为空
            objects = goodsSearchDTO.getPage();
        }

        // 设置 search_after 参数
        searchSourceBuilder.size(SEARCH_GOODS_PAGE_NUM);

        if (Objects.equals(goodsSearchDTO.getSortType(), SORT_TYPE_ID)){
            // 1 默认按id排序
            SortBuilder<FieldSortBuilder> sortByIdBuilder = SortBuilders.fieldSort(GOODS_ID_FIELD).order(SortOrder.ASC);
            searchSourceBuilder.sort(sortByIdBuilder);
        }else if (Objects.equals(goodsSearchDTO.getSortType(), SORT_TYPE_PRICE)){
            // 2 按价格排序
            SortBuilder<FieldSortBuilder> sortByPriceBuilder = SortBuilders.fieldSort(GOODS_PRICE_FIELD).order(SortOrder.ASC);
            searchSourceBuilder.sort(sortByPriceBuilder);
        }else if (Objects.equals(goodsSearchDTO.getSortType(), SORT_TYPE_SALES)){
            // 3 按销量排序
            SortBuilder<FieldSortBuilder> sortBySalesBuilder = SortBuilders.fieldSort(GOODS_SALES_FIELD).order(SortOrder.DESC);
            searchSourceBuilder.sort(sortBySalesBuilder);
        }
        // 设置 search_after 参数
        searchSourceBuilder.searchAfter(objects);

        // 设置返回数量
        searchSourceBuilder.size(SEARCH_GOODS_PAGE_NUM);

        // 将搜索源构建器添加到搜索请求
        searchRequest.source(query);
        SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);

        // 解析搜索结果
        List<Goods> goodsList = new ArrayList<>();
        SearchHit[] hitsList = searchResponse.getHits().getHits();

        // 非空判断
        if (hitsList.length == 0) {
            // 没有搜索结果，更新最后文档值为初始值
            if (Objects.equals(goodsSearchDTO.getSortType(), SORT_TYPE_ID)){
                objects = new Object[]{0};
            }else if (Objects.equals(goodsSearchDTO.getSortType(), SORT_TYPE_PRICE)){
                objects = new Object[]{0.00};
            }else if (Objects.equals(goodsSearchDTO.getSortType(), SORT_TYPE_SALES)){
                objects = new Object[]{Integer.MAX_VALUE};
            }
        }

        // 获取搜索结果
        for (SearchHit hit : hitsList) {
            String sourceAsString = hit.getSourceAsString();
            // 将搜索结果转换为 JSON 对象,并赋值到Goods 对象中
            JSONObject jsonObject = JSON.parseObject(sourceAsString);
            Goods goods= Goods.builder()
                    .id(Long.valueOf(jsonObject.getString(GOODS_ID_FIELD)))
                    .goodsName(jsonObject.getString(GOODS_NAME_FIELD))
                    .goodsImage(jsonObject.getString(GOODS_IMAGE_FIELD))
                    .goodsPriceMin(jsonObject.getBigDecimal(GOODS_PRICE_FIELD))
                    .goodsPriceMax(jsonObject.getBigDecimal(GOODS_PRICE_FIELD))
                    .stockTotal(jsonObject.getIntValue(GOODS_STOCK_FIELD))
                    .goodsSales(jsonObject.getIntValue(GOODS_SALES_FIELD))
                    .build();
            goodsList.add(goods);
        }

        // 获取最后一个文档的 sort 值
        SearchHits hits = searchResponse.getHits();
        SearchHit[] searchHits = hits.getHits();
        Object[] sortValues = new Object[0];
        if (searchHits.length > 0) {
            // 获取最后一个文档的 sort 值
            SearchHit lastHit = searchHits[searchHits.length - 1];
            sortValues = lastHit.getSortValues();

            // 打印 sort 值
            for (Object value : sortValues) {
                System.out.println(value);
            }
        }

        // 封装结果
        GoodsSearchVO goodsSearchVO = GoodsSearchVO.builder()
                .list(new HomeDataListVO(goodsList))
                .sortValues(sortValues)
                .build();
        return goodsSearchVO;
    }

}
